package main;

import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;

import static util.Constants.FP_TOLERANCE;

/**
 * The Camera class describes a camera that determines the view of
 * the scene displayed on the screen.
 *
 * @author Jeran Fox
 * @author Matthew Cong
 * @author Teddy Ni
 * @author Yunfeng Bai
 */
public class Camera
{
    /**
     * Keeps track of camera directions, because we use them so often.
     * These should be kept as unit vectors.
     */
	private String name;
	private Point3d position;
    private Vector3d cameraLeft;
    private Vector3d cameraForward;
    private Vector3d cameraUp;

    /**
     * Create a camera at given position in world coordinates.
     *
     * @param Point3d location of center of mass (world frame)
     */
    public Camera(Point3d position_)
    {
        this(null, position_);
    }

    /**
     * Create a camera at given position with given name.
     *
     * @param String name that identifies this object
     * @param Point3d location of center of mass (world frame)
     */
    public Camera(String name_, Point3d position_)
    {
        name = name_;
        position = position_;

        // By default, set up default camera directions.
        cameraForward = new Vector3d(position);
        cameraForward.negate();
        cameraForward.normalize();
        cameraUp = new Vector3d(0.0, 1.0, 0.0);
        cameraLeft = new Vector3d();
        cameraLeft.cross(cameraUp, cameraForward);
    }

    /**
     * Get camera upward direction.
     *
     * @return Vector3d
     */
    public Vector3d getUp()
    {
        assert cameraUp != null;
        assert Math.abs(cameraUp.length() - 1) < FP_TOLERANCE;

        return cameraUp;
    }

    /**
     * Get camera left direction.
     *
     * @return Vector3d
     */
    public Vector3d getLeft()
    {
        assert cameraLeft != null;
        assert Math.abs(cameraLeft.length() - 1) < FP_TOLERANCE;

        return cameraLeft;
    }

    /**
     * Get camera forward direction.
     *
     * @return Vector3d
     */
    public Vector3d getForward()
    {
        assert cameraForward != null;
        assert Math.abs(cameraForward.length() - 1) < FP_TOLERANCE;

        return cameraForward;
    }

    public void freeMove(double dx_, double dy_, double dz_)
    {
		if (Math.abs(dx_) < FP_TOLERANCE && Math.abs(dy_) < FP_TOLERANCE && Math.abs(dz_) < FP_TOLERANCE)
        {
            return;
        }

		position.x += (dx_ * cameraLeft.x + dy_ * cameraUp.x + dz_ * cameraForward.x);
		position.y += (dx_ * cameraLeft.y + dy_ * cameraUp.y + dz_ * cameraForward.y);
		position.z += (dx_ * cameraLeft.z + dy_ * cameraUp.z + dz_ * cameraForward.z);
    }

    public void rotateX(double theta_)
    {
		double angle = (theta_ * Math.PI) / 180.0;

		Vector3d u = new Vector3d();
		u.scale(-Math.sin(angle), cameraUp);
		Vector3d v = new Vector3d();
		v.scale(Math.cos(angle), cameraForward);

		cameraForward.add(u, v);
		cameraForward.normalize();

		cameraLeft.cross(cameraUp, cameraForward);
		cameraLeft.normalize();
    }

    public void rotateY(double theta_)
    {
		double angle = (theta_ * Math.PI) / 180.0;

		Vector3d u = new Vector3d();
		u.scale(Math.sin(angle), cameraLeft);
		Vector3d v = new Vector3d();
		v.scale(Math.cos(angle), cameraForward);

		cameraForward.add(u, v);
		cameraForward.normalize();

		cameraLeft.cross(cameraUp, cameraForward);
		cameraLeft.normalize();
    }

    public void rotateZ(double theta_)
    {
    }

	public Point3d getPosition()
	{
		return position;
	}
}
